package com.msh.artascope.devcloud.service.service.impl;


import com.alibaba.fastjson.JSON;
import com.msh.artascope.devcloud.client.po.DefalutResourcePo;
import com.msh.artascope.devcloud.client.po.EnvPo;
import com.msh.artascope.devcloud.client.po.LogConfigPo;
import com.msh.artascope.devcloud.client.po.ModuleNetworkPo;
import com.msh.artascope.devcloud.client.po.ModulePo;
import com.msh.artascope.devcloud.client.po.ModuleResourcePo;
import com.msh.artascope.devcloud.client.qo.EnvQo;
import com.msh.artascope.devcloud.client.qo.HealthTestQo;
import com.msh.artascope.devcloud.client.qo.ModuleBuildQo;
import com.msh.artascope.devcloud.client.qo.ModuleNetworkQo;
import com.msh.artascope.devcloud.client.qo.ModuleQo;
import com.msh.artascope.devcloud.client.qo.ModuleResourceQo;
import com.msh.artascope.devcloud.client.qo.ModuleRunQo;
import com.msh.artascope.devcloud.service.dao.ModuleDao;
import com.msh.artascope.devcloud.service.define.DevCloudDef;
import com.msh.artascope.devcloud.service.model.page.ModuleBuildRunPageVo;
import com.msh.artascope.devcloud.service.model.page.ModuleListBuildRunPageVo;
import com.msh.artascope.devcloud.service.model.query.ModuleQueryVo;
import com.msh.artascope.devcloud.service.model.query.ModuleRunQueryVo;
import com.msh.artascope.devcloud.service.model.vo.ModuleRunVo;
import com.msh.artascope.devcloud.service.model.vo.ModuleVo;
import com.msh.artascope.devcloud.service.service.DefalutResourceService;
import com.msh.artascope.devcloud.service.service.EnvService;
import com.msh.artascope.devcloud.service.service.HealthTestService;
import com.msh.artascope.devcloud.service.service.KubernetesConfigService;
import com.msh.artascope.devcloud.service.service.LogConfigService;
import com.msh.artascope.devcloud.service.service.ModuleBuildService;
import com.msh.artascope.devcloud.service.service.ModuleNetworkService;
import com.msh.artascope.devcloud.service.service.ModuleResourceService;
import com.msh.artascope.devcloud.service.service.ModuleReverseProxyService;
import com.msh.artascope.devcloud.service.service.ModuleRunService;
import com.msh.artascope.devcloud.service.service.ModuleService;
import com.msh.artascope.devcloud.service.util.FreemarkerUtil;
import com.msh.artascope.devcloud.service.wrapper.K8sWrapper;
import com.msh.artascope.devcloud.service.wrapper.K8sWrapper.K8sInfo;
import com.msh.artascope.devcloud.service.wrapper.model.PodInfo;
import com.msh.controller.starter.fuzzy.search.model.qo.FuzzySearchQo;
import com.msh.controller.starter.fuzzy.search.service.FuzzySearchService;
import com.msh.frame.client.base.SortBy;
import com.msh.frame.client.base.TenantBaseServiceImpl;
import com.msh.frame.client.common.Result;
import com.msh.frame.client.define.ColumnDef;
import com.msh.frame.client.define.StringDef;
import com.msh.frame.client.enums.SortModeEnum;
import com.msh.frame.client.exception.BizException;
import com.msh.frame.interfaces.IdGenerateable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

/**
 * @author shihu
 * @mail m-sh@qq.com
 */
@Service
@Slf4j
public class ModuleServiceImpl extends TenantBaseServiceImpl<ModulePo,ModuleQo> implements ModuleService {

  @Resource
  private ModuleDao moduleDao;

  @Resource
  private IdGenerateable idGenerateable;

  @Autowired
  @Lazy
  private ModuleBuildService moduleBuildService;

  @Autowired
  @Lazy
  private ModuleRunService moduleRunService;

  @Autowired
  @Lazy
  private HealthTestService healthTestService;

  @Autowired
  @Lazy
  private ModuleResourceService moduleResourceService;

  @Resource
  @Lazy
  private ModuleNetworkService moduleNetworkService;

  @Resource
  private K8sWrapper k8sWrapper;

  @Resource
  private FuzzySearchService fuzzySearchService;

  @Resource
  private KubernetesConfigService kubernetesConfigService;

  @Resource
  private DefalutResourceService defalutResourceService;

  @Resource
  private LogConfigService logConfigService;

  @Resource
  private EnvService envService;
  @Resource
  @Lazy
  private ModuleReverseProxyService moduleReverseProxyService;

  private static final String ELK_PATH_FORMAT = "http://192.168.1.221:5601/app/discover#/?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))&_a=(columns:!(message,beat.name),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:'58d98860-5f5a-11ed-8836-b9ca3394dc11',key:fields.app.keyword,negate:!f,params:(query:admin-server),type:phrase),query:(match_phrase:(fields.app.keyword:%s)))),index:'58d98860-5f5a-11ed-8836-b9ca3394dc11',interval:auto,query:(language:kuery,query:''),sort:!(!('@timestamp',desc)))";

  @Override
  public ModuleListBuildRunPageVo moduleListBuildRunPageVo(Long projectId, Long moduleId,
      Long tenantId) {
    if (null == moduleId) {
      return moduleListBuildRunPageVo(projectId, null);
    }
    ModulePo modulePo = get(moduleId, tenantId);
    BizException.doThrowIfNull("未获取到模块信息", modulePo);
    BizException.doThrowIfTrue("越权操作", !modulePo.getTenantId().equals(tenantId));
    modulePo.setTenantId(tenantId);
    return moduleListBuildRunPageVo(projectId, modulePo);
  }

  @Override
  public ModuleListBuildRunPageVo moduleListBuildRunPageVo(Long projectId, ModulePo modulePo) {
    ModuleListBuildRunPageVo moduleListBuildRunPageVo = new ModuleListBuildRunPageVo();
    List<ModulePo> modulePoList = this.list(new ModuleQo().setProjectId(projectId).setSortBy(SortBy.asc(ColumnDef.NAME)));
    moduleListBuildRunPageVo.setModuleList(modulePoList);
    if(CollectionUtils.isEmpty(modulePoList)){
      moduleListBuildRunPageVo.setModuleBuildRun(new ModuleBuildRunPageVo());
      return moduleListBuildRunPageVo;
    }
    if(null == modulePo){
      modulePo = modulePoList.get(0);
    }
    ModuleBuildRunPageVo moduleBuildRunPageVo = this.moduleBuildRunPageVo(modulePo);
    moduleListBuildRunPageVo.setModuleBuildRun(moduleBuildRunPageVo);
    return moduleListBuildRunPageVo;
  }

  @Override
  public ModuleBuildRunPageVo moduleBuildRunPageVo(Long moduleId, Long tenantId) {
    ModulePo modulePo = get(moduleId, tenantId);
    BizException.doThrowIfNull("未获取到模块信息", modulePo);
    BizException.doThrowIfTrue("越权操作", !modulePo.getTenantId().equals(tenantId));
    modulePo.setTenantId(tenantId);
    return moduleBuildRunPageVo(modulePo);
  }

  private ModuleBuildRunPageVo moduleBuildRunPageVo(ModulePo modulePo) {
    ModuleBuildRunPageVo moduleBuildRun = new ModuleBuildRunPageVo();
    setLogUrl(modulePo, moduleBuildRun);
    moduleBuildRun.setModule(modulePo);
    moduleBuildRun.setModuleBuild(
        moduleBuildService.listOne(
            new ModuleBuildQo()
                .setModuleId(modulePo.getId())
                .setPageSize(1)
                .setSortBy(new SortBy(ColumnDef.ID, SortModeEnum.DESC))
        )
    );
    ModuleRunQueryVo moduleRunQueryVo = (ModuleRunQueryVo)new ModuleRunQueryVo()
        .setModuleId(modulePo.getId())
        .setPageSize(1)
        .setSortBy(new SortBy(ColumnDef.ID, SortModeEnum.DESC));
    List<ModuleRunVo> moduleRunVoS = moduleRunService.listVo(moduleRunQueryVo);
    if(!CollectionUtils.isEmpty(moduleRunVoS)){
      moduleBuildRun.setModuleRun(moduleRunVoS.get(0));
    }
    return moduleBuildRun;
  }

  /**
   * 设置日志地址
   * @param modulePo
   * @param moduleBuildRun
   */
  private void setLogUrl(ModulePo modulePo, ModuleBuildRunPageVo moduleBuildRun){
    Long tenantId = modulePo.getTenantId();
    LogConfigPo logConfigPo = logConfigService.getByEnvId(modulePo.getEnvId(), tenantId);
    if(null == logConfigPo){
      moduleBuildRun.setLogUrl(StringDef.EMPTY);
      return;
    }
    if(StringUtils.isEmpty(logConfigPo.getLogUrl())){
      moduleBuildRun.setLogUrl(StringDef.EMPTY);
      return;
    }
    EnvPo envPo = envService.get(modulePo.getEnvId(), tenantId);
    BizException.doThrowIfNull("未查询到环境信息", envPo);

    Map<String, String> map = new HashMap<>(16);
    map.put("moduleName", modulePo.getName());
    map.put("namespace", envPo.getNamespace());
    try {
      String logUrl = logConfigPo.getLogUrl();
      logUrl = FreemarkerUtil.stringfreemarkerProcess(map, logUrl);
      moduleBuildRun.setLogUrl(logUrl);
    } catch (Exception e) {
      log.warn("字符串解析异常, str: {}, map: {}", logConfigPo.getLogUrl(), JSON.toJSON(map), e);
    }
  }

  @Override
  public long insert(ModulePo param) {
    EnvPo envPo = envService.get(param.getEnvId(), param.getTenantId());
    BizException.doThrowIfNull("未查询到环境信息", envPo);
    return insert(param, envPo);
  }

  @Override
  public long insert(ModulePo param, EnvPo envPo) {
    param.setId(idGenerateable.getUniqueId());
    ModulePo modulePo = listOne(
            new ModuleQo().setName(param.getName())
                    .setEnvId(param.getEnvId()));
    BizException.doThrowIfTrue("制品名重复", null != modulePo);
    long insert = super.insert(param);
    fuzzySearchService.insertCollection(param.getName(), DevCloudDef.FUZZY_SEARCH_TYPE_MODULE_NAME,
        insert, param.getName() + "-" + envPo.getEnv(), param.getTenantId());
    return insert;
  }

  @Override
  public long insertVo(ModulePo param) {
    DefalutResourcePo defalutResourcePo = defalutResourceService
            .getByEnvId(param.getEnvId(), param.getTenantId());
    BizException.doThrowIfNull("缺少默认资源配置", defalutResourcePo);
    EnvPo envPo = envService.get(param.getEnvId(), param.getTenantId());
    BizException.doThrowIfNull("未查询到环境信息", envPo);
    return insertWithResource(param, defalutResourcePo, envPo);
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public void flushSearch(Long tenantId) {
    fuzzySearchService.deleteByQo(new FuzzySearchQo().setTenantId(tenantId));
    List<ModulePo> list = list(new ModuleQo().setTenantId(tenantId));
    Map<Long, EnvPo> map = envService.map(new EnvQo().setTenantId(tenantId));
    list.stream().forEach(m->{
      EnvPo envPo = map.get(m.getEnvId());
      if(null == envPo){
        return;
      }
      fuzzySearchService.insertCollection(m.getName(), DevCloudDef.FUZZY_SEARCH_TYPE_MODULE_NAME,
          m.getId(), m.getName() + "-" + envPo.getEnv(), m.getTenantId());
    });
  }

  private long insertWithResource(ModulePo param, DefalutResourcePo defalutResourcePo, EnvPo envPo) {
    long insert = insert(param, envPo);
    ModuleResourcePo moduleResourcePo = new ModuleResourcePo();
    moduleResourcePo.setDiskSize(defalutResourcePo.getDiskSize());
    moduleResourcePo.setMaxMem(defalutResourcePo.getMaxMem());
    moduleResourcePo.setMinMem(defalutResourcePo.getMinMem());
    if("exec_jar".equals(param.getType())){
      moduleResourcePo.setJavaArgs(defalutResourcePo.getJavaArgs());
    }
    moduleResourcePo.setReplicasNum(defalutResourcePo.getReplicasNum());
    moduleResourcePo.setModuleId(insert);
    moduleResourcePo.setProjectId(param.getProjectId());
    moduleResourcePo.setTenantId(param.getTenantId());
    moduleResourceService.insert(moduleResourcePo);
    return insert;
  }

  @Override
  public int insertCollection(Collection<ModulePo> param) {
    param.stream().forEach(p->p.setId(idGenerateable.getUniqueId()));
    return super.insertCollection(param);
  }


  @Override
  public int delete(Long id, Long tenantId) {
    ModulePo modulePo = this.get(id, tenantId);
    if(null == modulePo){
      return 0;
    }
    K8sInfo k8s = kubernetesConfigService.getK8s(modulePo.getEnvId(), tenantId);
    BizException.doThrowIfNull("缺少k8s配置信息", k8s);
    if(!modulePo.getTenantId().equals(tenantId)){
      return 0;
    }
    fuzzySearchService.delete(DevCloudDef.FUZZY_SEARCH_TYPE_MODULE_NAME, id, tenantId);
    List<PodInfo> pods = k8sWrapper.getPods(k8s, modulePo);
    BizException.doThrowIfTrue("应用正在运行，无法删除", !CollectionUtils.isEmpty(pods));
    int delete = super.delete(id, tenantId);
    moduleReverseProxyService.deleteByModuleId(id, tenantId);
    ModuleNetworkPo moduleNetworkPo = moduleNetworkService.listOne(new ModuleNetworkQo().setModuleId(id).setTenantId(tenantId));
    if(null != moduleNetworkPo){
      k8sWrapper.deleteService(k8s, modulePo);
      k8sWrapper.deleteIstioVirtualService(k8s, modulePo.getName());
      moduleNetworkService.delete(moduleNetworkPo.getId(), tenantId);
    }
    moduleResourceService.deleteByQo(new ModuleResourceQo().setModuleId(id), tenantId);
    moduleBuildService.deleteByQo(new ModuleBuildQo().setModuleId(id), tenantId);
    moduleRunService.deleteByQo(new ModuleRunQo().setModuleId(id), tenantId);
    healthTestService.deleteByQo(new HealthTestQo().setModuleId(id), tenantId);
    return delete;
  }


// ----------前端vo处理-------------------------------------------------------

  private ModuleVo toVo(ModulePo po){
    ModuleVo vo = po.convert(ModuleVo.class);
    return vo;
  }

  private List<ModuleVo> toVoList(List<ModulePo> list){
    if(CollectionUtils.isEmpty(list)){
      return Collections.emptyList();
    }

    return list.stream().map(po->{
      return toVo(po);
    }).collect(Collectors.toList());
  }

  @Override
  public ModuleVo getVo(Long id, Long tenantId) {
    ModulePo po = this.get(id, tenantId);
    if(null == po){
      return null;
    }
    ModuleVo vo = toVo(po);

    return vo;
  }

  @Override
  public List<ModuleVo> listVo(ModuleQueryVo q){
    ModuleQo qo = q.convert(ModuleQo.class);
    List<ModulePo> poList = this.list(qo);
    List<ModuleVo> voList = toVoList(poList);

    return voList;
  }

  @Override
  public Result<List<ModuleVo>> pageVo(ModuleQueryVo param) {
    ModuleQo qo = param.convert(ModuleQo.class);
    int count = this.count(qo);
    if (0 == count) {
      return Result.successReturn(Collections.<ModuleVo>emptyList()).setCount(count);
    }
    List<ModulePo> poList = this.list(qo);
    List<ModuleVo> voList = toVoList(poList);

    return Result.successReturn(voList).setCount(count);
  }


}
